#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "../../clsv/error.h"
#include "ipc/msgADT.h"
#include "ipc/tcpADT.h"
#include "ipc/shmADT.h"
#include "ipc/fifoADT.h"
#include "transADT.h"

extern int errno;
/*--------------------------------------------------------------------------*/

struct transCDT
{
	int type; 		/* Transport type to use */
	void *extra;	/* For special use of each transport type */
};

/*--------------------------------------------------------------------------*/

int initTransADT(int type, transADT *trans);

/*--------------------------------------------------------------------------*/

/* Common initialization of transADT */
int
initTransADT(int type, transADT *trans)
{
	transADT aux;

	*trans = NULL;

	if ( (aux = malloc(sizeof(struct transCDT)) ) == NULL )
	{
		fprintf(stderr,"transADT: Initiating transADT, malloc error.\n");
		return ERROR_MALLOC; /* Not enough memory */
	}
	
	aux->type = type;
	aux->extra = NULL;
	
	*trans = aux;
	
	return ERROR_OK;

}

/*--------------------------------------------------------------------------*/

int
connTrans(int type, const void *dest, transADT *trans)
{
	Ttcp *tcp;
	Tshm *shm;
	Tfifo *fifo;
	int error = ERROR_OK;
	
	error = initTransADT(type, trans);
	if( error != ERROR_OK )
		return error;

	switch( type )
	{	
		case TRANS_FIFO:
			fifo = (Tfifo *)dest;
			error = connFifo(fifo->read, fifo->write, (fifoADT *)&((*trans)->extra) );
			break;
		case TRANS_MSG:
			error = connMsg(*(int *)dest, (msgADT *)&((*trans)->extra) );
			break;
		case TRANS_SHM:
			shm = (Tshm *)dest;
			error = connShm(shm->keySnd, shm->keyRcv, (shmADT *)&((*trans)->extra) );
			break;
		case TRANS_TCP:
			tcp = (Ttcp *)dest;
			error = connTcp(tcp->host, tcp->port, (tcpADT *)&((*trans)->extra) );
			break;
		default:
			error = ERROR_PARAMS;
			fprintf(stderr,"transADT: Connect, invalid type parameter.\n");
			break;
	}

	/* If error free memory */
	if( error != ERROR_OK )
	{
		free(*trans);
	}

	return error;
}

/*--------------------------------------------------------------------------*/

int bindTrans(int type, const void *dest, transADT *trans)
{
	Tshm *shm;
	Tfifo *fifo;
	int error = ERROR_OK;

	error = initTransADT(type, trans);
	if( error != ERROR_OK )
		return error;

	switch( type )
	{	
		case TRANS_FIFO:
			fifo = (Tfifo *)dest;
			error = bindFifo(fifo->read, fifo->write, (fifoADT *)&((*trans)->extra) );
			break;
		case TRANS_MSG:
			error = bindMsg(*(int *)dest, (msgADT *)&((*trans)->extra));
			break;
		case TRANS_SHM:
			shm = (Tshm *)dest;
			error = bindShm(shm->keySnd, shm->keyRcv, (shmADT *)&((*trans)->extra) );
			break;
		case TRANS_TCP:
			error = bindTcp((char *)dest, (tcpADT *)&((*trans)->extra) );
			break;
		default:
			error = ERROR_PARAMS;
			fprintf(stderr,"transADT: Bind, invalid type parameter.\n");
			break;
	}

	/* If error free memory */
	if( error != ERROR_OK )
	{
		free(*trans);
	}

	return error;
}

/*--------------------------------------------------------------------------*/

int
acceptTrans(transADT trans, transADT *new)
{
	int error = ERROR_OK;

	error = initTransADT(trans->type, new);
	if( error != ERROR_OK )
		return error;

	switch( trans->type )
	{	
		case TRANS_FIFO:
			error = acceptFifo(trans->extra, (fifoADT *)&((*new)->extra));
			break;
		case TRANS_MSG:
			error = acceptMsg(trans->extra, (msgADT *)&((*new)->extra));
			break;
		case TRANS_SHM:
			error = acceptShm(trans->extra, (shmADT *)&((*new)->extra));
			break;
		case TRANS_TCP:
			error = acceptTcp(trans->extra, (tcpADT *)&((*new)->extra));
			break;
		default:
			error = ERROR_PARAMS;
			fprintf(stderr,"transADT: Accept, invalid type parameter.\n");
			break;
	}
//	printf("acceptTrans: salgo de aca\n");
//	if(errno == EINTR)
//		printf("acceptTrans: EINTR\n");
	return error;
}

/*--------------------------------------------------------------------------*/

int rcvTrans(transADT trans, void *buf, int count, int timeO)
{
	int error = ERROR_OK;
	//printf("rcvTrans: entro \n");
	switch( trans->type )
	{	
		case TRANS_FIFO:
			error = rcvFifo(trans->extra, buf, count);
			break;
		case TRANS_MSG:
			error = rcvMsg(trans->extra, buf, count);
			break;
		case TRANS_SHM:
			error = rcvShm(trans->extra, buf, count);
			break;
		case TRANS_TCP:
			error = rcvTcp(trans->extra, buf, count, timeO);
			break;
		default:
			error = ERROR_PARAMS;
			fprintf(stderr,"transADT: Receiving, invalid type parameter.\n");
			break;
	}
	//printf("rcvTrans: salgo\n");
	return error;
}

/*--------------------------------------------------------------------------*/

int sndTrans(transADT trans, void *buf, int count)
{
	int error = ERROR_OK;

	switch( trans->type )
	{	
		case TRANS_FIFO:
			error = sndFifo(trans->extra, buf, count);
			break;
		case TRANS_MSG:
			error = sndMsg(trans->extra, buf, count);
			break;
		case TRANS_SHM:
			error = sndShm(trans->extra, buf, count);
			break;
		case TRANS_TCP:
			error = sndTcp(trans->extra, buf, count);
			break;
		default:
			error = ERROR_PARAMS;
			fprintf(stderr,"transADT: Sending, invalid type parameter.\n");
			break;
	}

	return error;
}

/*--------------------------------------------------------------------------*/

int closeTrans(transADT trans)
{
	int error = ERROR_OK;

	if( trans != NULL )
	{
		switch( trans->type )
		{	
			case TRANS_FIFO:
				error = closeFifo(trans->extra);
				break;
			case TRANS_MSG:
				error = closeMsg(trans->extra);
				break;
			case TRANS_SHM:
				error = closeShm(trans->extra);
				break;
			case TRANS_TCP:
				error = closeTcp(trans->extra);
				break;
			default:
				error = ERROR_PARAMS;
				fprintf(stderr,"transADT: Closing, invalid type parameter.\n");
				break;
		}
	
		free(trans);
	}

	return error;
}
